﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using GurLoader.Util;
using Imgur.Net;
using Imgur.Net.Auth;

namespace GurLoader.Service.ImgurTaskBuilder
{
    public class ImgurTaskBuilderService : ITaskBuilderService
    {
        public ImgurTaskBuilderService(IImageProvider imageProvider, IAlbumProvider albumProvider)
        {
            ImageProvider = imageProvider;
            AlbumProvider = albumProvider;
        }

        public IImageProvider ImageProvider { get; private set; }
        public IAlbumProvider AlbumProvider { get; private set; }

        public Task<ImgurProcessResult> UploadAlbum(IEnumerable<ImageContent> images, string title, string description,
            LayoutStyle layoutStyle, IProgress<IDelegatingCommandProgress> progress, CancellationToken cancellationToken, bool disposeImages, IDispatcher dispatcher, bool openWhenComplete)
        {
            var commandProgress = new DelegatingCommandProgress(Command.SendAlbum, 0,
                "Starting album creation...", dispatcher);

            progress.Report(commandProgress);

            var uploadTask = new Task<ImgurProcessResult>(() =>
            {
                #region Image tasks

                var imageUploadTasks = new List<Task<ImageUploadResult>>();
                var imageUploadStatusTasks = new List<Task<ImageUploadResult>>();

                foreach (ImageContent image in images)
                {
                    var imgProgress = new CommandProgress(Command.SendImage, 0, "Starting image upload...");

                    imgProgress.ContentDescription = image.Description;

                    commandProgress.AddChild(imgProgress);
                    
                    progress.Report(commandProgress);

                    var imageTask = new Task<ImageUploadResult>(() =>
                    {
                        ImageInfo imgInfo = ImageProvider.Provider.UploadImage(image.ImageData, null, null);
                        return new ImageUploadResult(imgInfo);
                    });
                    imageUploadTasks.Add(imageTask);

                    
                    Task<ImageUploadResult> imageStatusTask = imageTask.ContinueWith(antecedents =>
                    {
                        imgProgress.ProgressPercentage = 100;
                        imgProgress.Status = "Image upload complete.";
                        imgProgress.Result = antecedents.Result;

                        progress.Report(commandProgress);

                        if (disposeImages)
                            image.ImageData.Dispose();

                        return antecedents.Result;
                    }, cancellationToken);

                    imageUploadStatusTasks.Add(imageStatusTask);
                }

                #endregion

                foreach (var imageUploadTask in imageUploadTasks)
                {
                    imageUploadTask.Start();
                }

                #region Album Upload Task

                Task<AlbumUploadResult> createAlbumTask = Task.Factory.ContinueWhenAll<AlbumUploadResult>(
                    imageUploadStatusTasks.ToArray(),
                    antecedents =>
                    {
                        AlbumInfo albumInfo = AlbumProvider.Provider.CreateAlbum(
                            (from task in imageUploadTasks select task.Result.ImageInfo.Id).ToList(), title, description,
                            PrivacyLevel.Public, layoutStyle, null);

                        return new AlbumUploadResult(albumInfo);
                    });

                Task<AlbumUploadResult> createAlbumStatusTask = createAlbumTask.ContinueWith(antecedents =>
                {
                    commandProgress.ProgressPercentage = 100;
                    commandProgress.Status = "Album creation complete.";
                    commandProgress.Result = antecedents.Result;

                    progress.Report(commandProgress);

                    return antecedents.Result;
                }, cancellationToken);

                #endregion

                //createAlbumTask.Start();
                try
                {
                    createAlbumStatusTask.Wait(cancellationToken);
                    //createAlbumTask.Wait(cancellationToken);

                    if (openWhenComplete)
                        Process.Start(
                            LinkHelper.GetLinkUrl(createAlbumStatusTask.Result.AlbumInfo));
                }
                catch (OperationCanceledException)
                {
                    Debug.WriteLine("Cancelling...");
                    commandProgress.Status = "Operation cancelled.";
                    commandProgress.IsCancelled = true;
                    foreach (CommandProgress child in commandProgress.Children)
                    {
                        if (! child.IsComplete)
                            child.Status = "Operation cancelled.";

                        child.IsCancelled = true;
                    }

                    progress.Report(commandProgress);
                }

                return createAlbumStatusTask.Result;
            });

            return uploadTask;
        }

        public Task<ImgurProcessResult> UploadImages(IEnumerable<ImageContent> images,
            IProgress<IDelegatingCommandProgress> progress, CancellationToken cancellationToken, bool disposeImages, IDispatcher dispatcher, bool openWhenComplete)
        {
            var commandProgress = new DelegatingCommandProgress(Command.SendAlbum, 0,
                "Starting album creation...", dispatcher);

            var uploadTask = new Task<ImgurProcessResult>(() =>
            {
                #region Image tasks

                var imageUploadTasks = new List<Task<ImageUploadResult>>();
                var imageUploadStatusTasks = new List<Task<ImageUploadResult>>();

                foreach (ImageContent image in images)
                {
                    var imgProgress = new CommandProgress(Command.SendImage, 0, "Starting image upload...");

                    imgProgress.ContentDescription = image.Description;

                    commandProgress.AddChild(imgProgress);

                    progress.Report(commandProgress);

                    var imageTask = new Task<ImageUploadResult>(() =>
                    {
                        try
                        {
                            ImageInfo imgInfo = ImageProvider.Provider.UploadImage(image.ImageData, null, null);
                            return new ImageUploadResult(imgInfo);
                        }
                        catch (Exception e)
                        {
                            Debug.WriteLine(e.Message);
                            return null;
                        }
                    });
                    imageUploadTasks.Add(imageTask);

                    Task<ImageUploadResult> imageStatusTask = imageTask.ContinueWith(antecedents =>
                    {
                        imgProgress.ProgressPercentage = 100;
                        imgProgress.Status = "Image upload complete.";
                        imgProgress.Result = antecedents.Result;

                        progress.Report(commandProgress);

                        if ( disposeImages )
                            image.ImageData.Dispose(); 

                        return antecedents.Result;
                    }, cancellationToken);

                    imageUploadStatusTasks.Add(imageStatusTask);
                }

                #endregion

                Task<BatchImageUploadResult> parentUploadTask =
                    Task.Factory.ContinueWhenAll<BatchImageUploadResult>(imageUploadStatusTasks.ToArray(),
                        antecedents =>
                        {
                            commandProgress.ProgressPercentage = 100;
                            commandProgress.Status = "Image uploading complete.";

                            progress.Report(commandProgress);

                            return
                                new BatchImageUploadResult(
                                    (from task in imageUploadStatusTasks select task.Result).ToList());
                        });

                try
                {
                    foreach (var imageUploadTask in imageUploadTasks)
                    {
                        imageUploadTask.Start();
                    }

                    parentUploadTask.Wait(cancellationToken);
                    if (openWhenComplete)
                        Process.Start(
                            LinkHelper.GetLinkUrl(parentUploadTask.Result.Results.First().ImageInfo));
                }
                catch (OperationCanceledException)
                {
                    Debug.WriteLine("Cancelling...");
                    commandProgress.Status = "Operation cancelled.";
                    commandProgress.IsCancelled = true;
                    foreach (CommandProgress child in commandProgress.Children)
                    {
                        if (! child.IsComplete)
                            child.Status = "Operation cancelled.";

                        child.IsCancelled = true;
                    }

                    progress.Report(commandProgress);
                }

                return parentUploadTask.Result;
            });

            return uploadTask;
        }
    }
}